﻿// 使用注意，为每个文件都新建一个ExcelOP对象，用完置null
#if ENABLE_EXCEL_COM
#define ENABLE_AutoItX
namespace excelOp
{
    using System;
    using System.Diagnostics;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using Microsoft.Office.Interop.Excel;

    public class ExcelOP
    {
        protected Microsoft.Office.Interop.Excel.Application m_app;
        protected Microsoft.Office.Interop.Excel.Workbooks m_workbooks;
        protected Microsoft.Office.Interop.Excel.Workbook m_workbook;

        private tool.CTimer m_timer;
        protected string m_sCurFilename;
        protected string m_sCurPassword;
        private autoIt.CAutoIt m_wndKiller;
        protected object MISS_VALUE = System.Reflection.Missing.Value;

        // 如果不想使用AutoIt关闭对话框则_killWndTool置null
        public void Init(autoIt.CAutoIt _killWndTool, bool bOpenTime)
        {
            if(_killWndTool != null)
            {
                m_wndKiller = _killWndTool;
                m_wndKiller.AddBlockFunc(DoShellBlock);
            }
            if(bOpenTime)
            {
                m_timer = new tool.CTimer(KillCurApp);
            }
        }

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int nPID);
        protected bool IsStable()
        {
            if (m_app == null)
            {
                return true;
            }
            int nPid = 0;
            IntPtr pWnd = new IntPtr(m_app.Hwnd);
            GetWindowThreadProcessId(pWnd, out nPid);
            System.Diagnostics.Process proc = System.Diagnostics.Process.GetProcessById(nPid);
            // 比较两秒前后CPU时间是否变化
            long m1 = proc.WorkingSet64 / 1024;
            int t1 = proc.TotalProcessorTime.Seconds;
            System.Threading.Thread.Sleep(2000);
            long m2 = proc.WorkingSet64 / 1024;
            int t2 = proc.TotalProcessorTime.Seconds;

            if (t1 != t2)
            {
                return false;
            }

            return true;
        }

        public static void KillAllExcelProcess()
        {
            try
            {
                System.Diagnostics.Process[] processes =
                     System.Diagnostics.Process.GetProcessesByName("EXCEL");
                foreach (System.Diagnostics.Process proc in processes)
                {
                    proc.Kill();
                }
            }
            catch (System.Exception ex)
            {
                tool.Output.DumpWriteLine(ex.ToString());
            }
        }

        protected void KillCurApp()
        {
            int nPid = 0;
            IntPtr pWnd = new IntPtr(m_app.Hwnd);
            GetWindowThreadProcessId(pWnd, out nPid);
            System.Diagnostics.Process pExcel = System.Diagnostics.Process.GetProcessById(nPid);
            pExcel.Kill();
        }

        public static void RegisterExcel(string sExcelPath)
        {
            if (sExcelPath.Length == 0)
            {
                return;
            }
            try
            {
                if (!System.IO.Directory.Exists(sExcelPath))
                {
                    throw new tool.ETImpException(tool.LogTool.LOG_TYPE.ERROR,
                                            null, "Register Excel error!");
                }

                KillAllExcelProcess();
                ProcessStartInfo ExeFile = new ProcessStartInfo();
                ExeFile.FileName = sExcelPath + "\\EXCEL.exe";
                //ExeFile.FileName = "\"" + ExeFile.FileName + "\"";
                ExeFile.Arguments = "/regserver";
                Process Proc = Process.Start(ExeFile);
                Proc.WaitForExit();
            }
            catch (Exception ex)
            {
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.ERROR,
                          null, "Register Excel error!", ex);
            }
        }

        public void DoShellBlock(bool bUnknown)
        {
            try
            {
                if(bUnknown)
                {
                    m_workbook = null;
                    m_workbooks = null;
                    m_app = null;
                    tool.LogTool.GetInstance().AddLog(tool.LogTool.LOG_TYPE.SHELL_BLOCK, m_sCurFilename,
                        "program is blocked by unknown reason.", null);
                }
                else
                {
                    Close();
                    tool.CAddInfoHlp.AddFileIgnore(m_sCurFilename, ET_Improve.Global.s_curCmdName, tool.LogTool.LOG_TYPE.SHELL_BLOCK);
                }
            }
            catch (System.Exception ex)
            {
                tool.LogTool.GetInstance().AddLog(tool.LogTool.LOG_TYPE.ERROR, m_sCurFilename, "DoShellBlock失败", ex);
            }
        }

        public void Open(string sFileName, string sPassword)
        {
            Open(sFileName, sPassword, true);
        }

        public void Open(string sFileName, string sPassword, bool bReadOnly)
        {
            PreCreateExcel();
            //m_app = new Microsoft.Office.Interop.Excel.ApplicationClass();
            m_app = new Microsoft.Office.Interop.Excel.Application();
            m_app.Visible = false;
            m_workbooks = m_app.Workbooks;
            m_sCurFilename = sFileName;
            m_sCurPassword = sPassword;
            
            if (m_timer != null)
            {
                m_timer.BeginTimer(ET_Improve.Global.gTimeOut * 60, false);
            }

            try
            {
                m_workbook = m_workbooks.Open(sFileName, 0, bReadOnly, MISS_VALUE,
                              (m_sCurPassword == null) ? MISS_VALUE : m_sCurPassword,
                              MISS_VALUE, MISS_VALUE, MISS_VALUE, MISS_VALUE, true, false,
                              MISS_VALUE, false, MISS_VALUE, MISS_VALUE);
                m_workbook.EnableAutoRecover = false;
            }
            catch (tool.ETImpException ex)
            {
                ex.AddLog();
            }
            catch (System.Exception ex)
            {
                if (m_wndKiller != null && m_wndKiller.IsNeedPassword())
                {
                    m_wndKiller.ResetWndInfo();
                    throw new tool.ETImpException(tool.LogTool.LOG_TYPE.NO_PASSWORD, sFileName, "EXCEL");
                }
                else if (m_timer.IsTimeOut())
                {
                    throw new tool.ETImpException(tool.LogTool.LOG_TYPE.TIME_OUT, sFileName, "EXCEL");
                }
                else
                {
                    throw new tool.ETImpException(tool.LogTool.LOG_TYPE.OPEN_FAILED, sFileName, "EXCEL", ex);
                }
            }
            finally
            {
                if(m_timer != null)
                {
                    m_timer.EndTimer();
                }
            }
        }

        // 当发现进程中excel进程多于10个，则killAll
        protected void PreCreateExcel()
        {
            System.Diagnostics.Process[] processes =
                    System.Diagnostics.Process.GetProcessesByName("excel");
            if (processes.Length >= 10)
            {
                foreach (System.Diagnostics.Process p in processes)
                {
                    p.Kill();
                }
            }
        }

        // 不抛出任何异常
        public void Close()
        {
            try
            {
                tool.Output.DumpWriteLine(">Excel Closing<");
                ReleaseWorkbook();
                NAR(m_workbooks);
                ReleaseApp();
                tool.Output.DumpWriteLine(">Closed<");
            }
            catch (System.Exception ex)
            {
                tool.LogTool.GetInstance().AddLog(tool.LogTool.LOG_TYPE.EXCEPTION, null, "Excel.Close 可能是无关紧要的异常" + ex.Message, null);
            }
        }

        public bool IsRecalculate()
        {
            try
            {
                return !m_workbook.Saved;
            }
            catch (System.Exception ex)
            {
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.ERROR, m_sCurFilename, "error:m_workbook.Saved", ex);
            }
        }

        public void SaveAs(string sDstFile)
        {
            try
            {
                m_workbook.SaveAs(sDstFile, MISS_VALUE,
                    (m_sCurPassword == null) ? MISS_VALUE : m_sCurPassword,
                    MISS_VALUE, MISS_VALUE, false, XlSaveAsAccessMode.xlNoChange,
                    false, MISS_VALUE, MISS_VALUE, MISS_VALUE, MISS_VALUE);
            }
            catch (System.Exception ex)
            {
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.ERROR, m_sCurFilename, "Excel Save失败", ex);
            }
        }

        protected void ReleaseWorkbook()
        {
            if (m_workbook == null)
            {
                return;
            }
            try
            {
                m_workbook.Saved = true;    //  不显示保存对话框
                m_workbook.Close(false, null, null);
            }
            catch { }
            finally
            {
                NAR(m_workbook);
            }
        }

        protected void ReleaseApp()
        {
            if (m_app == null)
            {
                return;
            }
            try
            {
                m_app.Quit();
            }
            catch
            {

            }
            finally
            {
                NAR(m_app);
            }
        }

        protected static void NAR(object o)
        {
            if (o == null)
            {
                return;
            }

            try
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(o);
            }
            catch { }
            finally
            {
                o = null;
            }
        }

        public static void DestoryExcelOp(ref ExcelOP pExcel)
        {
            if(pExcel != null)
            {
                pExcel.Close();
                pExcel = null;
            }
        }
    }
}



#endif  // #if ENABLE_EXCEL_COM